#include "stdafx.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ObjBase.h>
#include <wchar.h>
#include "win_base.h"
#include "h2log.h"

#pragma comment(lib, "Ole32.lib")


LRESULT CALLBACK WinBaseProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    win_base* pWinBase = reinterpret_cast<win_base*>(GetWindowLong(hWnd, GWL_USERDATA));
    if (pWinBase)
    {
        return pWinBase->win_proc(hWnd, uMsg, wParam, lParam);
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}


win_base::win_base()
{
    _hInstance = GetModuleHandle(NULL);
    _dwStyle = WS_OVERLAPPEDWINDOW;
	_dwExStyle = WS_EX_TOOLWINDOW;
    wcscpy_s(_szTitle, L"");
    _ptPos.x = _ptScreenPos.x = CW_USEDEFAULT;
    _ptPos.y = _ptScreenPos.y = CW_USEDEFAULT;
    _iWidth = CW_USEDEFAULT;
    _iHeight = CW_USEDEFAULT;
    _oldProc = DefWindowProc;
    _oldUserData = 0;
    _hParent = NULL;
    _hWnd = NULL;
}

win_base::~win_base()
{
    if(_hWnd)
    {
        DestroyWindow(_hWnd);
    }
}

void win_base::move_window(int x, int y, int width, int height, BOOL repaint)
{
    if(width >= 0)
    {
        _iWidth = width;       
    }
    if(height >= 0)
    {
        _iHeight = height;
    }
    ::MoveWindow(_hWnd, x, y, _iWidth, _iHeight, repaint);
}

void win_base::show_window(bool state)
{
    ::ShowWindow(_hWnd, state ? SW_SHOW : SW_HIDE);
}


bool win_base::reg_class(HINSTANCE hInstance, PWCHAR pszClassName)
{
	WNDCLASSEX wcex;

    if(hInstance)
    {
        _hInstance = hInstance;   
    }
    else if(NULL == _hInstance)
    {
        _hInstance = GetModuleHandle(NULL);
    }

    if(NULL != pszClassName)
    {
        wcscpy_s(_szClassName, pszClassName);
    }
    else
    {
        // Generate GUID as the class name
        GUID guidApp;
        ::CoCreateGuid(&guidApp);
        ::StringFromGUID2(guidApp, _szClassName, sizeof(_szClassName));
    }

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	wcex.lpfnWndProc	= WinBaseProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= _hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= _szClassName;
	wcex.hIconSm		= NULL;

    int iResult = RegisterClassEx(&wcex);

    STRING::ansi classname(_szClassName);
    LOG::debug(DEBUG_WIN | DEBUG_L1, __FUNCTION__ "() register class '%s' returns %d.",
        *classname, iResult);

    return iResult != 0;
}


bool win_base::create(HWND hWndParent)
{
    if(!_hInstance)
    {
        _hInstance = GetModuleHandle(NULL);
    }

    if(hWndParent)
    {
        _hParent = hWndParent;
    }

    _hWnd = CreateWindowEx(_dwExStyle, _szClassName, _szTitle, _dwStyle,
        _ptPos.x, _ptPos.y, _iWidth, _iHeight, _hParent, NULL, _hInstance, NULL);

    STRING::ansi classname(_szClassName);
    LOG::debug(DEBUG_WIN | DEBUG_L1, __FUNCTION__ "() create window of class '%s' returns %08X.",
        *classname, _hWnd);

    if(!_hWnd)
    {
        return false;
    }

    ::SetWindowLong(_hWnd, GWL_USERDATA, reinterpret_cast<LONG>(this));
    return true;
}

void win_base::destroy_window()
{
    ::DestroyWindow(_hWnd);
    _hWnd = NULL;
}


bool win_base::sub_class(HWND hWnd)
{
    if(NULL == hWnd)
    {
		LOG::error(ERR_CODE_PARAM, __FILE__ " line %d: " __FUNCTION__ "() hWnd is NULL!", __LINE__);
        return false;
    }

    // Fill private attributes with the attached windows
    _hWnd = hWnd;
    GetWindowText(_hWnd, _szTitle, sizeof(_szTitle));
    _hInstance = reinterpret_cast<HINSTANCE>(GetWindowLong(_hWnd, GWL_HINSTANCE));
    _oldProc = reinterpret_cast<WNDPROC>(GetWindowLong(_hWnd, GWL_WNDPROC));
    _oldUserData = GetWindowLong(_hWnd, GWL_USERDATA);
    _dwStyle = GetWindowLong(_hWnd, GWL_STYLE);
    RECT rc;
    GetWindowRect(_hWnd, &rc);

    // Hook frame WNDPROC and set USERDATA
    SetWindowLong(_hWnd, GWL_USERDATA, reinterpret_cast<LONG>(this));
    SetWindowLong(_hWnd, GWL_WNDPROC, reinterpret_cast<LONG>(WinBaseProc));

    STRING::ansi classname(_szClassName);
    LOG::debug(DEBUG_WIN | DEBUG_L1, __FUNCTION__ "() subclass window %08X as class '%s'.",
        _hWnd, *classname);

    return true;
}

bool win_base::visible()
{
    return true;
}

bool win_base::minimized()
{
	WINDOWPLACEMENT wp;
	wp.length = sizeof(WINDOWPLACEMENT);
	GetWindowPlacement(_hWnd, &wp);
	return wp.showCmd == SW_SHOWMINIMIZED;
}


bool win_base::maximized()
{
	WINDOWPLACEMENT wp;
	wp.length = sizeof(WINDOWPLACEMENT);
	GetWindowPlacement(_hWnd, &wp);
	return wp.showCmd == SW_SHOWMAXIMIZED;
}


void win_base::set_title(LPCWSTR szTitle)
{
    wcscpy_s(_szTitle, szTitle);
    SetWindowText(_hWnd, _szTitle);
}

void win_base::set_exstyle(DWORD dwExStyle)
{
    _dwExStyle = dwExStyle;
    SetWindowLong(_hWnd, GWL_EXSTYLE, _dwExStyle);
}

void win_base::set_style(DWORD dwStyle)
{
    _dwStyle = dwStyle;
    SetWindowLong(_hWnd, GWL_STYLE, _dwStyle);
}


LRESULT win_base::win_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

	_uMsg = uMsg;
	_wParam = wParam;
	_lParam = lParam;

	switch(uMsg)
	{
	case WM_ACTIVATE:
		return OnActivate(LOWORD(wParam), LOWORD(wParam)&WA_CLICKACTIVE, HIWORD(wParam), (HWND)lParam);
	case WM_ACTIVATEAPP:
        return OnActivateApp(wParam, lParam);
    case WM_CANCELMODE:
		return OnCancelMode();
    case WM_CHAR:
		return OnChar(wParam, lParam);
    case WM_CHILDACTIVATE:
		return OnChildActivate();
	case WM_CLOSE:
		return OnClose();
	case WM_COMMAND:
		return OnCommand(HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
	case WM_DESTROY:
		return OnDestroy();
	case WM_DRAWITEM:
		return OnDrawItem(wParam, (DRAWITEMSTRUCT *)lParam);
	case WM_ENABLE:
		return OnEnable(wParam);
	case WM_ENTERSIZEMOVE:
		return OnEnterSizeMove();
	case WM_ERASEBKGND:
		return OnEraseBackground((HDC)wParam);
	case WM_EXITSIZEMOVE:
		return OnExitSizeMove();
	case WM_GETMINMAXINFO:
		return OnGetMinMaxInfo((MINMAXINFO*) lParam);
	case WM_HSCROLL:
		return OnHorScroll(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
	case WM_HELP:
		return OnHelp((HELPINFO*)lParam);
	case WM_KEYDOWN:
		return OnKeyDown(wParam, lParam);
	case WM_KEYUP:
		return OnKeyUp(wParam, lParam);
	case WM_KILLFOCUS:
		return OnKillFocus((HWND) wParam);
	case WM_LBUTTONDBLCLK:
		return OnLeftDoubleClick(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
	case WM_LBUTTONDOWN:
		return OnLeftDown(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
	case WM_LBUTTONUP:
		return OnLeftUp(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
	case WM_MOUSEACTIVATE:
		return OnMouseActivate((HWND) wParam, HIWORD (lParam), LOWORD (lParam));
	case WM_MOUSEMOVE:
		return OnMouseMove(wParam, LOWORD(lParam), HIWORD(lParam));
	case WM_MOUSEWHEEL:
		return OnMouseWheel(LOWORD(lParam), HIWORD(lParam), LOWORD(wParam), HIWORD(wParam));
	case WM_NCACTIVATE:
		return OnNcActivate(wParam, (HWND)lParam);
	case WM_NCCALCSIZE:
		return OnNcCalcSize((BOOL) wParam, lParam);
	case WM_NCHITTEST:
        return OnNcHitTest(LOWORD(lParam), HIWORD(lParam));
    case WM_NCLBUTTONDBLCLK:
		return OnNcLeftDoubleClick(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
	case WM_NCLBUTTONDOWN: { POINTS p = MAKEPOINTS(lParam); 
		return OnNcLeftDown(p.x, p.y, wParam); }
	case WM_NCLBUTTONUP: { POINTS p = MAKEPOINTS(lParam); 
		return OnNcLeftUp(p.x, p.y, wParam); }
	case WM_NCRBUTTONDOWN: { POINTS p = MAKEPOINTS(lParam); 
		return OnNcRightDown(p.x, p.y, wParam); }
	case WM_NCRBUTTONUP: { POINTS p = MAKEPOINTS(lParam); 
		return OnNcRightUp(p.x, p.y, wParam); }
	case WM_NOTIFY:
		return OnNotify((INT)wParam, (NMHDR *)lParam);
	case WM_PAINT:
        return OnPaint();
	case WM_PASTE:
        return OnPaste();
	case WM_VSCROLL:
		return OnVerScroll(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
	case WM_RBUTTONDBLCLK:
		return OnRightDoubleClick(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
	case WM_RBUTTONDOWN:
		return OnRightDown(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
	case WM_RBUTTONUP:
		return OnRightUp(LOWORD(lParam), HIWORD(lParam), wParam&MK_CONTROL, wParam&MK_SHIFT);
    case WM_SETCURSOR:
		return OnSetCursor(hWnd, LOWORD (lParam), HIWORD (lParam));
    case WM_SETFOCUS:
	    return OnSetFocus((HWND) wParam);
	case WM_SETTINGCHANGE:
		return OnSettingChange(wParam, (LPCTSTR) lParam);
	case WM_SHOWWINDOW:
		return OnShowWindow(wParam, lParam);
	case WM_SIZE:
	    return OnSize(wParam, LOWORD(lParam), HIWORD(lParam));
    case WM_SIZING:
		return OnSizing(wParam, (RECT *)lParam);
    case WM_SYSCOMMAND:
		return OnSysCommand(wParam, LOWORD(lParam), HIWORD(lParam));
	case WM_TIMER:
		return OnTimer(wParam, (TIMERPROC *)lParam);
    case WM_WINDOWPOSCHANGED:
        { 
            WINDOWPOS * pos = (WINDOWPOS *) lParam;
            _ptPos.x = _ptScreenPos.x = pos->x;
            _ptPos.y = _ptScreenPos.y = pos->y;
            _iWidth = pos->cx;
            _iHeight = pos->cy;
            if(get_parent())
            {
                ClientToScreen(get_parent(), &_ptScreenPos);
            }
            return OnWindowPosChanged(pos);
        }
    case WM_WINDOWPOSCHANGING:
        return OnWindowPosChanging((WINDOWPOS *)lParam);
		 
	default:
		return _oldProc(_hWnd, _uMsg, _wParam, _lParam);
	}

	return _oldProc(_hWnd, _uMsg, _wParam, _lParam);
}
